#include <glad/glad.h> // 顺序不能换
#include <imgui/imgui.h>
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <mylib/shader.h>
#include <mylib/camera.h>
#include <mylib/tools.h>

#include <iostream>

void processInput(GLFWwindow *window);
void framebuffer_size_callback(GLFWwindow *windwow, int width, int height); // 处理窗口大小变化
void mouse_callback(GLFWwindow *window, double xpos, double ypos); // 鼠标输入回调函数
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset); // 鼠标滚轮

/* 自由移动的摄像机 */
Camera camera(glm::vec3(0, 0, 3));
/* 光源位置 */
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
glm::vec3 lightColor(1, 1, 1);
glm::vec3 lightDir(0, 0, -1.0f);
float lightCutoff = 15.0f;
float softDeg = 5.0f;
float lightOuterCutoff = lightCutoff + softDeg;
/* 时间差 */
float deltaTime = 0.0f; // 当前帧与上一帧的时间差
float lastFrame = 0.0f; // 上一帧的时间
// 窗口宽高
float screenWidth = 1600;
float screenHeight = 900;
// 光标初始位置
float lastX = screenWidth / 2, lastY = screenHeight / 2;

// 鼠标是否显示
bool gameMode = true;
int main(/*int argc, char *argv[]*/)
{
    camera.SetMoveSpeed(5.0f);

    // 初始化GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 次版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式
    GLFWwindow *window = glfwCreateWindow(screenWidth, screenHeight, "Learn OpenGL", NULL, NULL);
    if (window == NULL){
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window); // 将窗口上下文切换至当前上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 当帧缓冲区大小变化时，重新调整glViewport的大小
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){ // 初始化GLAD
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    // 初始化 ImGui 上下文
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO &io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
    ImGui_ImplGlfw_InitForOpenGL(window, true);      // Second param install_callback=true will install GLFW callbacks and chain to existing ones.
    ImGui_ImplOpenGL3_Init("#version 330 core");

    Shader shader("src/shader/vertex.vert", "src/shader/fragment.frag");
    Shader lightShader("src/shader/lightvertex.vert", "src/shader/lightfragment.frag");
    glViewport(0, 0, screenWidth, screenHeight); // 设置OpenGL在窗口的显示区域
    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
    // 立方体的36个顶点及其法向量
    float vertexs[] = {
        // positions          // normals           // texture coords
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
        -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,

        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
        -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,

        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,

        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,

        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};
    // 渲染顺序，尽在按元素渲染时使用
    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };
    
    
    // 顶点数组对象，
    unsigned int VAO;
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);
    // 顶点缓冲对象，存储顶点信息
    unsigned int VBO;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexs), vertexs, GL_STATIC_DRAW);
    // 顶点信息定义，并激活
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(3 * sizeof(float)));
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(6 * sizeof(float)));
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    // 灯的顶点数组对象
    unsigned int lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0); // 设置位置属性
    glEnableVertexAttribArray(0);
    // glEnableVertexAttribArray(1);

    // 模型坐标空间到世界坐标空间
    glm::mat4 model(1.0f);
    // 获取矩阵位置
    unsigned int transformLOC = glGetUniformLocation(shader.program, "transform");
    unsigned int modelLOC = glGetUniformLocation(shader.program, "model");
    unsigned int modelNormalLOC = glGetUniformLocation(shader.program, "modelNormal");
    unsigned int viewLOC = glGetUniformLocation(shader.program, "view");
    unsigned int projectionLOC = glGetUniformLocation(shader.program, "projection");
    
    Texture diffuseTex("image/container2.png");
    Texture specularTex("image/container2_specular.png");
    int diffuseTexId = 0, specularTexId = 1;
    shader.Use();
    // 设置材质TEXCOODR'X'
    shader.SetVec3("lightColor", lightColor);
    shader.SetVec3("objectColor", 1.0f, 0.5f, 0.31f);
    // shader.SetVec3("material.ambient", 1.0f, 0.5f, 0.31f);
    shader.SetInt("material.diffuse", diffuseTexId);
    shader.SetInt("material.specular", specularTexId);

    shader.SetVec3("material.specular", 0.5f, 0.5f, 0.5f);
    shader.SetFloat("material.shininess", 32.0f);
    /* 平行光 */
    // shader.SetVec3("light.direction", lightDir);
    // shader.SetVec3("light.ambient", 0.2f, 0.2f, 0.2f);
    // shader.SetVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // 将光照调暗了一些以搭配场景
    // shader.SetVec3("light.specular", 1.0f, 1.0f, 1.0f);
    /* 点光源 */
    // shader.SetVec3("light.position", lightPos);
    // shader.SetVec3("light.ambient", 0.2f, 0.2f, 0.2f);
    // shader.SetVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // 将光照调暗了一些以搭配场景
    // shader.SetVec3("light.specular", 1.0f, 1.0f, 1.0f);
    // shader.SetFloat("light.constant", 1.0f);
    // shader.SetFloat("light.linear", 0.09f);
    // shader.SetFloat("light.quadratic", 0.032f);
    /* 聚光灯 */
    shader.SetVec3("light.direction", lightDir);
    shader.SetFloat("light.cutOff", glm::cos(lightCutoff));
    shader.SetVec3("light.position", lightPos);
    shader.SetVec3("light.ambient", 0.2f, 0.2f, 0.2f);
    shader.SetVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // 将光照调暗了一些以搭配场景
    shader.SetVec3("light.specular", 1.0f, 1.0f, 1.0f);
    shader.SetFloat("light.constant", 1.0f);
    shader.SetFloat("light.linear", 0.09f);
    shader.SetFloat("light.quadratic", 0.032f);
    // 传递矩阵
    glEnable(GL_DEPTH_TEST);
    // 绘制10个同样的立方体，提前设置好不同的位置
    glm::vec3 cubePositions[] = {
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(2.0f, 5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3(2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f, 3.0f, -7.5f),
        glm::vec3(1.3f, -2.0f, -2.5f),
        glm::vec3(1.5f, 2.0f, -2.5f),
        glm::vec3(1.5f, 0.2f, -1.5f),
        glm::vec3(-1.3f, 1.0f, -1.5f)
    };
    /* IMGUI */
    float lightPosUI[3] = {lightPos[0], lightPos[1], lightPos[2]};
    float lightDirUI[3] = {lightDir[0], lightDir[1], lightDir[2]};
    float lightCutoffUI = lightCutoff;
    float lightSoftDegUI = softDeg;
    while (!glfwWindowShouldClose(window))
    {
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
        // ImGui::ShowDemoWindow(); // Show demo window! :)
        ImGuiWindowFlags window_flags = 0;
        window_flags |= ImGuiWindowFlags_NoCollapse;
        if(ImGui::Begin("My Window", NULL, window_flags)){
            ImGui::SeparatorText("Light Transform");
            ImGui::DragFloat3("Light Pos", lightPosUI, 0.01f, -1e10, 1e10, "%.2f");
            ImGui::DragFloat3("Light Dir", lightDirUI, 0.01f, -1e10, 1e10, "%.2f");
            ImGui::DragFloat("Light Cutoff", &lightCutoffUI, 0.01f, 0, 90, "%.2f");
            ImGui::DragFloat("Light softDeg", &lightSoftDegUI, 0.01f, 0, 90 - lightCutoff, "%.2f");
        }
        ImGui::End();
        for (int i = 0; i < 3;i++){
            lightPos[i] = lightPosUI[i];
            lightDir[i] = lightDirUI[i];
        }
        lightCutoff = lightCutoffUI;
        lightOuterCutoff = lightCutoffUI + lightSoftDegUI;
        processInput(window);
        // 清除颜色缓冲和深度缓冲
        glClearColor(0, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        /* 计算时间差 */
        float currentTime = glfwGetTime();
        deltaTime = currentTime - lastFrame;
        lastFrame = currentTime;

        // shader.SetVec3("light.position", lightPos.x, lightPos.y, lightPos.z);
        // trans = glm::scale(trans, glm::vec3(0.5f, 0.5f, 0.5f));
        glm::mat4 projection(1.0f);
        // 确立一个视锥体的基本信息
        projection = glm::perspective(glm::radians(camera.fov), screenWidth / screenHeight, 1.0f, 100.0f);

        glm::mat4 view = camera.GetViewMat(); // 给定相机位置和目标以及世界上计算相机变换矩阵，笔记中CG有记载
        // std::cout << camera.position.x;
        shader.Use();
        // lightColor.x = abs(sin(glfwGetTime() * 2.0f));
        // lightColor.y = abs(sin(glfwGetTime() * 0.7f));
        // lightColor.z = abs(sin(glfwGetTime() * 1.3f));
        Texture::BindTexCood(diffuseTexId, diffuseTex);
        Texture::BindTexCood(specularTexId, specularTex);
        shader.SetVec3("light.ambient", lightColor * 0.1f);
        /*点光源*/
        shader.SetVec3("light.position", lightPos);
        /*点光源*/
        /*平行光*/
        shader.SetVec3("light.direction", lightDir);
        /*平行光*/
        /*聚光灯*/
        shader.SetFloat("light.cutOff", glm::cos(glm::radians(lightCutoff)));
        shader.SetFloat("light.outerCutOff", glm::cos(glm::radians(lightOuterCutoff)));
        // std::cout << lightCutoff << "#" << lightOuterCutoff << std::endl;
        /*聚光灯*/

        shader.SetVec3("light.diffuse", lightColor * 1.0f); // 将光照调暗了一些以搭配场景
        float strangth = glm::dot(glm::vec3(1.0f, 1.0f, 1.0f), lightColor) / 2;
        shader.SetVec3("light.specular", strangth, strangth, strangth);
        // shader.SetVec3("light.specular", lightColor);
        shader.SetVec3("lightColor", lightColor);
        shader.SetVec3("viewPos", camera.position);

        glUniformMatrix4fv(viewLOC, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projectionLOC, 1, GL_FALSE, glm::value_ptr(projection));
        glBindVertexArray(VAO);
        for (int i = 0; i < 10; i++)
        {
            model = glm::mat4(1.0f);
            model = glm::translate(model, cubePositions[i]);
            model = glm::rotate(model, glm::radians(20.0f * i * (float)glfwGetTime()), glm::vec3(0.2f, 0.3f, 0.5f));
            // 传递矩阵
            glm::mat4 modelNormal = glm::transpose(glm::inverse(model));
            glUniformMatrix4fv(modelNormalLOC, 1, GL_FALSE, glm::value_ptr(modelNormal));
            glUniformMatrix4fv(modelLOC, 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        // glDrawArrays(GL_TRIANGLES, 0, 36);
        
        lightShader.Use();
        model = glm::mat4(1.0f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f));
        lightShader.SetVec3("lightColor", lightColor);
        glUniformMatrix4fv(modelLOC, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLOC, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projectionLOC, 1, GL_FALSE, glm::value_ptr(projection));
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        // Rendering
        // (Your code clears your framebuffer, renders your other stuff etc.)
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        // (Your code calls glfwSwapBuffers() etc.)
        // glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glfwSwapBuffers(window); // 交换前后缓冲
        glfwPollEvents();        // 获得窗口事件
    }
    // 内存释放
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glfwTerminate();
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    return 0;
}

void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    glViewport(0, 0, width, height);
} // 回调函数窗口大小（帧缓冲）改变

bool left_alt_released = true;
void processInput(GLFWwindow* window){ // 按下ESC后关闭窗口
    if (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS)
    {
        if (!left_alt_released){
            return;
        }
        if (gameMode)
        {
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // 设置捕捉鼠标
            gameMode = !gameMode;
        }
        else
        {
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // 设置捕捉鼠标
            gameMode = !gameMode;
        }
        left_alt_released = false;
    }else{
        left_alt_released = true;
    }
    if (gameMode)
        return;
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS){
        glfwSetWindowShouldClose(window, true);
    }
    // float cameraSpeed = 2.0f * deltaTime; // adjust accordingly
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
    camera.ProcessMovement(CameraMovement::FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessMovement(CameraMovement::BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessMovement(CameraMovement::LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessMovement(CameraMovement::RIGHT, deltaTime);
}

void mouse_callback(GLFWwindow *window, double xpos, double ypos){
    if(gameMode)
        return;
    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // 注意这里是相反的，因为y坐标是从底部往顶部依次增大的
    lastX = xpos;
    lastY = ypos;

    camera.ProcessCameraRotate(xoffset, yoffset);
}

void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
    if (gameMode)
        return;
    camera.ProcessMouseScroll(yoffset);
}
